/*
 * Copyright (c) 2017-present, CV4J Contributors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.cv4j.core.datamodel;

import ohos.media.image.ImagePacker;
import ohos.media.image.ImageSource;
import ohos.media.image.PixelMap;
import ohos.media.image.common.PixelFormat;
import ohos.media.image.common.Rect;
import ohos.media.image.common.Size;

import com.cv4j.exception.CV4JException;
import com.cv4j.image.util.IOUtils;
import com.cv4j.uitl.Log;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;

/**
 * CV4JImage
 *
 * @since 2021-07-22
 */
public class CV4JImage implements ImageData, Serializable {
    private static final long serialVersionUID = -8832812623741546452L;
    private int width;
    private int height;
    private ImageProcessor processor;
    private PixelMap bitmap;


    public CV4JImage(PixelMap bitmap) {
        if (bitmap == null) {
            throw new CV4JException("bitmap is null");
        }

        width = bitmap.getImageInfo().size.width;
        height = bitmap.getImageInfo().size.height;

        int[] input = new int[width * height];
        bitmap.readPixels(input, 0, width, new Rect(0, 0, width, height));

        processor = new ColorProcessor(input, width, height);
        ((ColorProcessor) processor).setCallBack(this);
        input = null;
    }

    public CV4JImage(InputStream inputStream) {
        if (inputStream == null) {
            throw new CV4JException("inputStream is null");
        }
        PixelMap bitmap1 = null;
        try {
            ImageSource.SourceOptions sourceOptions = new ImageSource.SourceOptions();
            sourceOptions.formatHint = "image/png";
            ImageSource imageSource = ImageSource.create(inputStream, sourceOptions);
            ImageSource.DecodingOptions decodingOptions = new ImageSource.DecodingOptions();
            decodingOptions.desiredSize = new Size(0, 0);
            decodingOptions.desiredRegion = new Rect(0, 0, 0, 0);
            decodingOptions.desiredPixelFormat = PixelFormat.ARGB_8888;
            bitmap1 = imageSource.createPixelmap(decodingOptions);
        } catch (OutOfMemoryError e) {
            return;
        }

        width = bitmap1.getImageInfo().size.width;
        height = bitmap1.getImageInfo().size.height;
        int[] input = new int[width * height];
        bitmap1.readPixels(input, 0, width, new Rect(0, 0, width, height));
        processor = new ColorProcessor(input, width, height);
        ((ColorProcessor) processor).setCallBack(this);
        input = null;
        IOUtils.closeQuietly(inputStream);
    }

    public CV4JImage(byte[] bytes) {
        if (bytes == null) {
            throw new CV4JException("byte is null");
        }
        PixelMap bitmap1 = null;
        try {
            ImageSource.SourceOptions sourceOptions = new ImageSource.SourceOptions();
            sourceOptions.formatHint = "image/png";
            ImageSource imageSource = ImageSource.create(bytes, 0, bytes.length, sourceOptions);
            ImageSource.DecodingOptions decodingOptions = new ImageSource.DecodingOptions();
            decodingOptions.desiredSize = new Size(0, 0);
            decodingOptions.desiredRegion = new Rect(0, 0, 0, 0);
            decodingOptions.desiredPixelFormat = PixelFormat.ARGB_8888;
            bitmap1 = imageSource.createPixelmap(decodingOptions);
        } catch (OutOfMemoryError e) {
            return;
        }

        width = bitmap1.getImageInfo().size.width;
        height = bitmap1.getImageInfo().size.height;
        int[] input = new int[width * height];
        bitmap1.readPixels(input, 0, width, new Rect(0, 0, width, height));
        processor = new ColorProcessor(input, width, height);
        ((ColorProcessor) processor).setCallBack(this);
        input = null;
    }

    public CV4JImage(int width, int height) {
        this.width = width;
        this.height = height;
        processor = new ByteProcessor(new byte[width * height], width, height);
        ((ByteProcessor) processor).setCallBack(this);
    }

    public CV4JImage(int width, int height, int[] pixels) {
        this.width = width;
        this.height = height;
        processor = new ColorProcessor(pixels, width, height);
        ((ColorProcessor) processor).setCallBack(this);
        pixels = null;
    }

    public ImageProcessor getProcessor() {
        return this.processor;
    }

    @Override
    public CV4JImage convert2Gray() {
        if (processor instanceof ColorProcessor) {
            byte[] gray = new byte[width * height];
            int tr = 0;
            int tg = 0;
            int tb = 0;
            int c1 = 0;
            byte[] mR = ((ColorProcessor) processor).getRed();
            byte[] mG = ((ColorProcessor) processor).getGreen();
            byte[] mB = ((ColorProcessor) processor).getBlue();
            for (int i = 0; i < gray.length; i++) {
                tr = mR[i] & 0xff;
                tg = mG[i] & 0xff;
                tb = mB[i] & 0xff;
                c1 = (int) (0.299 * tr + 0.587 * tg + 0.114 * tb);
                gray[i] = (byte) c1;
            }
            processor = new ByteProcessor(gray, width, height);
            ((ByteProcessor) processor).setCallBack(this);
        }

        return this;
    }

    @Override
    public PixelMap toBitmap() {
        return toBitmap(PixelFormat.ARGB_8888);
    }

    @Override
    public PixelMap toBitmap(PixelFormat bitmapConfig) {
        if (bitmap != null) {
            return bitmap;
        }
        if (processor instanceof ColorProcessor || processor instanceof ByteProcessor) {
            PixelMap.InitializationOptions initializationOptions = new PixelMap.InitializationOptions();
            initializationOptions.pixelFormat = bitmapConfig;
            initializationOptions.size = new Size(width, height);
            initializationOptions.editable = true;
            bitmap = PixelMap.create(processor.getPixels(), initializationOptions);

        } else {
            // Exception
            Log.error("ColorImage", "can not convert to bitmap!");
        }
        return bitmap;
    }


    @Override
    public void setBitmap(PixelMap bitmap) {
        width = bitmap.getImageInfo().size.width;
        height = bitmap.getImageInfo().size.height;
        int[] input = new int[width * height];
        bitmap.readPixels(input, 0, width, new Rect(0, 0, width, height));
        processor = new ColorProcessor(input, width, height);
        ((ColorProcessor) processor).setCallBack(this);
        input = null;
    }

    /**
     * resetBitmap
     */
    public void resetBitmap() {
        this.bitmap = null;
    }

    /**
     * 保存图片到指定路径
     *
     * @param bitmap bitmap
     * @param format 支持jpg、png、webp
     * @param path path
     */
    public void savePic(PixelMap bitmap, String format, String path) {
        File file = new File(path);
        FileOutputStream out = null;
        try {
            out = new FileOutputStream(file);
            ImagePacker imagePacker = ImagePacker.create();
            ImagePacker.PackingOptions options = new ImagePacker.PackingOptions();
            options.quality = 100;
            options.format = format;
            imagePacker.initializePacking(out, options);
            imagePacker.addImage(bitmap);
            imagePacker.finalizePacking();
            out.flush();
            out.close();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 释放资源
     */
    public void recycle() {
        processor = null;
        bitmap = null;
    }
}
